<template>
  <el-dialog v-model="dialogVisible" title="运单详情" class="custom-dialog-class" top="65px" @opened="resetData">
    <div class="flex">
      <div class="left w-[50%]">
        <div id="waybill-map">
          <div
            class="absolute z-10 right-[6px] bottom-[12px] cursor-pointer bg-white p-[6px] font-medium shadow"
            @click="handleToVehicleTrack"
          >
            轨迹回放>>
          </div>
        </div>
        <v-chart id="chart" ref="chartRef" autoresize :option="echartOption" theme="light" />
      </div>
      <div class="w-[50%] overflow-y-hidden pl-[12px]">
        <div class="rounded-[8px] bg-slate-100">
          <div class="steps flex items-center px-[14px] relative">
            <div class="step" :class="{ active: orderDetail?.leaveTime }">
              <span class="status"></span>
              <div class="title">开始送货时间</div>
              <div class="time">{{ orderDetail?.leaveTime }}</div>
            </div>

            <p v-if="orderDetail?.arrivedDuration" class="describe">送达时长：{{ secToMins(orderDetail?.arrivedDuration) }}</p>

            <div class="step" :class="{ active: orderDetail?.arrivedTime }">
              <span class="status"></span>
              <div class="title">送达时间</div>
              <div class="time">{{ orderDetail?.arrivedTime }}</div>
            </div>

            <p v-if="orderDetail?.waitUnloadDuration" class="describe">等待时长：{{ secToMins(orderDetail?.waitUnloadDuration) }}</p>

            <div class="step" :class="{ active: orderDetail?.startUnloadTime }">
              <span class="status"></span>
              <div class="title">开始卸货时间</div>
              <div class="time">{{ orderDetail?.startUnloadTime }}</div>
            </div>

            <p v-if="orderDetail?.unloadDuration" class="describe">卸货时长：{{ secToMins(orderDetail?.unloadDuration) }}</p>

            <div class="step" :class="{ active: orderDetail?.unloadEndTime }">
              <span class="status"></span>
              <div class="title">卸货完成时间</div>
              <div class="time">{{ orderDetail?.unloadEndTime }}</div>
            </div>

            <p v-if="orderDetail?.returnDuration" class="describe">返程时长：{{ secToMins(orderDetail?.returnDuration) }}</p>

            <div class="step" :class="{ active: orderDetail?.returnEndTime }">
              <span class="status"></span>
              <div class="title">返程结束时间</div>
              <div class="time">{{ orderDetail?.returnEndTime }}</div>
            </div>
          </div>
        </div>
        <div class="leading-[50px] font-semibold">运单信息</div>
        <el-descriptions :column="2" border>
          <el-descriptions-item label="运单状态" align="center" label-class-name="w-[110px]" class-name="detail-content">
            <span v-if="orderDetail?.orderStatus === 0">装货中</span>
            <span v-else-if="orderDetail?.orderStatus === 1">送货中</span>
            <span v-else-if="orderDetail?.orderStatus === 2">待卸货(等待中)</span>
            <span v-else-if="orderDetail?.orderStatus === 3">卸货中</span>
            <span v-else-if="orderDetail?.orderStatus === 4">待签收</span>
            <span v-else-if="orderDetail?.orderStatus === 5">已签收</span>
          </el-descriptions-item>

          <el-descriptions-item label="强度等级" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.concreteStrengthGrade }}
          </el-descriptions-item>

          <el-descriptions-item label="运单编号" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.orderCode }}
          </el-descriptions-item>
          <el-descriptions-item label="塌落度" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.concreteSlump }}
          </el-descriptions-item>

          <el-descriptions-item label="关联订单" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.waybillCode }}
          </el-descriptions-item>
          <el-descriptions-item label="卸料方式" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.unloadWay }}
          </el-descriptions-item>

          <el-descriptions-item label="合同编号" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.contractCode }}
          </el-descriptions-item>
          <el-descriptions-item label="特殊要求" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.specialRequirements }}
          </el-descriptions-item>

          <el-descriptions-item label="所属站点" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.mixingStationName }}
          </el-descriptions-item>
          <el-descriptions-item label="实际方量" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.actualVolume }}
          </el-descriptions-item>

          <el-descriptions-item label="车牌号码" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.carNumber }}
          </el-descriptions-item>
          <el-descriptions-item label="票据数量" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.ticketVolume }}
          </el-descriptions-item>

          <el-descriptions-item label="搅拌车号" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.carCode }}
          </el-descriptions-item>
          <el-descriptions-item label="生产线" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.productionLine }}
          </el-descriptions-item>

          <el-descriptions-item label="司机姓名" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.driverName }}
          </el-descriptions-item>
          <el-descriptions-item label="运距[公里]" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.transDistance }}
          </el-descriptions-item>

          <el-descriptions-item label="司机电话" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.driverPhone }}
          </el-descriptions-item>
          <el-descriptions-item label="异常状态" align="center" label-class-name="w-[110px]" class-name="detail-content">
            <el-text v-if="orderDetail?.expStatus === 1" type="primary">正常</el-text>
            <template v-if="orderDetail?.expStatus === 2">
              <el-text type="danger">异常</el-text>
              <el-button type="primary" class="ml-8" link size="small" @click="handleCancelExpClick">取消异常</el-button>
            </template>
          </el-descriptions-item>

          <el-descriptions-item label="客户名称" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.customerName }}
          </el-descriptions-item>
          <el-descriptions-item label="异常备注" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.expStatus === 2 ? orderDetail?.expRemark : '' }}
          </el-descriptions-item>

          <el-descriptions-item label="工程名称" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.engineeringName }}
          </el-descriptions-item>
          <el-descriptions-item label="地址" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.actualDeliveryAddress }}
          </el-descriptions-item>

          <el-descriptions-item label="施工部位" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.buildPlace }}
          </el-descriptions-item>
          <el-descriptions-item label="卸货地址" align="center" label-class-name="w-[110px]" class-name="detail-content">
            {{ orderDetail?.receiveAddress }}
          </el-descriptions-item>
        </el-descriptions>
      </div>
    </div>
  </el-dialog>
</template>

<script lang="ts" setup>
import moment from 'moment'

import { wgs84ToGCJ02 } from '@/utils/mapUtil'
import { useMap } from '@/hooks/useMap/index'
import { routerNewWindow } from '@/utils/router'

import { getCarDataList } from '@/api/vehicle-track/vehicleTrack'
import type { VehicleTrackQuery } from '@/api/vehicle-track/vehicleTrack'

import { getWaybillDetailById, getWaybillDetailByCode, updateExpStatus, getWaybillRail } from '@/api/waybill/index'
import { WaybillDetail, RailData, LoadType } from '@/api/waybill/index'

import { createMapPolyline, createMarker, initRailType, createRails } from './map'
import { getInOutTimeArr, markChartRail, getCarSpeedLine } from './chart'

import MapCurrentCarMarkerSrc from '@/assets/images/map/runingCar.png'

interface Props {
  visible: boolean
  id?: string
  waybillCode?: string
}

const props = defineProps<Props>()

const emits = defineEmits(['update:visible'])

const dialogVisible = computed({
  get() {
    return props.visible
  },
  set(val) {
    emits('update:visible', val)
  }
})

const { map, initMap, AMap } = useMap('waybill-map', {
  manual: true,
  pluginsOption: {
    ToolBar: plugin => {
      return new plugin({ position: 'RT' })
    }
  }
})
// 跳转车辆轨迹
const handleToVehicleTrack = () => {
  routerNewWindow({
    name: 'VehicleTrack',
    query: {
      carNumber: orderDetail.value.carNumber,
      deviceId: orderDetail.value.deviceId,
      // 等待装货取创建时间
      startTime: orderDetail.value.leaveTime || orderDetail.value.createTime,
      endTime: orderDetail.value.unloadEndTime || undefined
    }
  })
}
/**
 * 取消异常
 */
const handleCancelExpClick = () => {
  ElMessageBox.confirm('确认取消当前运单的异常状态吗？', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消'
  }).then(async () => {
    // expStatus 异常状态: 1正常 2异常
    // 取消异常状态，变为正常
    await updateExpStatus({ id: orderDetail.value.id, expStatus: 1 })
    getDetail()
    ElMessage({
      type: 'success',
      message: '取消成功'
    })
  })
}

// 默认 tooltip 悬浮展示内容
const defaultEchartFormatter = (params: any) => {
  let len = params.length
  let res = ''
  for (let i = 0; i < len; i++) {
    res = res + params[i].marker + params[i].seriesName + ':' + `${params[i].data ? params[i].data : '-'}` + '</br>'
  }
  return res
}

const chartRef = ref()
const echartOption = ref({
  tooltip: {
    trigger: 'axis',
    axisPointer: {
      type: 'cross'
    },
    formatter: defaultEchartFormatter
  },
  grid: {
    top: 45,
    left: 40,
    right: 40,
    bottom: 40
  },
  legend: {
    data: [{ name: '速度', itemStyle: { color: '#98BDF5' } }]
  },
  xAxis: {
    type: 'category',
    boundaryGap: false,
    data: [],
    axisLabel: {
      formatter: function (params) {
        const labelArr = params.split(' ')
        const newParamsName = labelArr[0] + '\n' + labelArr[1]
        return newParamsName
      }
    }
  },
  yAxis: [
    {
      name: 'km/h',
      type: 'value',
      position: 'left',
      min: value => {
        Math.floor(value.min)
      },
      max: value => {
        Math.floor(value.Max)
      },
      splitNumber: 5,
      scale: true
    }
  ],
  series: [
    {
      type: 'line',
      name: '速度',
      symbol: 'none',
      yAxisIndex: 0,
      data: [],
      tooltip: {
        valueFormatter: function (value) {
          return value + 'km/h'
        }
      },
      lineStyle: {
        color: '#98BDF5'
      },
      areaStyle: {
        color: '#C2D9F6'
      },
      markArea: {
        data: []
      }
    }
  ],
  dataZoom: [
    {
      realtime: true
    },
    {
      type: 'inside',
      realtime: true
    }
  ]
})

const orderDetail = ref<WaybillDetail>(null) // 运单详情

const setEchartOptions = ({ xAxisData = [], speedData = [], markArea = [], formatterGen = () => defaultEchartFormatter }) => {
  const options = echartOption.value
  echartOption.value = {
    ...options,
    tooltip: {
      ...options.tooltip,
      // 更新 echart hover 联动地图方法
      formatter: formatterGen()
    },
    xAxis: {
      ...options.xAxis,
      data: xAxisData
    },
    series: [
      {
        type: 'line',
        name: '速度',
        symbol: 'none',
        yAxisIndex: 0,
        data: speedData,
        tooltip: {
          valueFormatter: function (value) {
            return value + 'km/h'
          }
        },
        lineStyle: {
          color: '#98BDF5'
        },
        areaStyle: {
          color: '#C2D9F6'
        },
        markArea: {
          data: markArea
        }
      }
    ]
  }
}

const resetData = async () => {
  // useMap 会在 onMounted 初始化地图
  // 此时 dialog 内部未加载
  // 第一次打开弹框，需要手动调用
  if (!map.value) await initMap()
  // 清除所有覆盖物
  map.value.clearMap()
  // 重置 echart
  setEchartOptions({})
  getDetail()
}

async function getDetail() {
  let method, param
  // id 优先级高
  if (props.id) {
    method = getWaybillDetailById
    param = {
      id: props.id
    }
  } else if (props.waybillCode) {
    method = getWaybillDetailByCode
    param = {
      orderCode: props.waybillCode
    }
  }
  const { result } = await method(param)
  orderDetail.value = result

  // leaveTime 开始送货时间
  // 如果接单还没送货，开始时间为创建时间
  // unloadEndTime 卸货完成时间 可能为空
  const { orderCode, leaveTime, createTime, unloadEndTime, deviceId } = result as WaybillDetail

  const [rails, [points, overlays]] = await Promise.all([
    getRailData(orderCode),
    getCarHistoryData({
      startTime: leaveTime || createTime,
      // 结束时间没有为 undefined
      // 应用解构赋值的默认值
      endTime: unloadEndTime || undefined,
      deviceId
    })
  ])
  // 默认缩放显示完整轨迹数据
  setView(overlays)

  let markArea = []

  rails.forEach(rail => {
    const {
      loadInTimeArr = [],
      loadOutTimeArr = [],
      unloadInTimeArr = [],
      unLoadOutTimeArr = []
    } = getInOutTimeArr(AMap.value, points, rail)
    // 写入值
    markArea = markChartRail({ loadInTimeArr, loadOutTimeArr, unloadInTimeArr, unLoadOutTimeArr }, markArea)
  })

  const { xAxisData, speedData } = getCarSpeedLine(points)
  // echart 悬浮提示方法
  // 保存轨迹数据，用于地图打点
  const formatterGen = () => {
    let marker = null
    return (params: any) => {
      const data = params[0]
      const point = points[data.dataIndex]
      const position = wgs84ToGCJ02(point.lng, point.lat)
      if (!marker) {
        marker = createCarMarker(position)
        map.value.add(marker)
      } else {
        updateCarMarker(position, marker)
      }
      return defaultEchartFormatter(params)
    }
  }
  setEchartOptions({ xAxisData, speedData, markArea, formatterGen })
}
// 创建 marker，使用自定义 marker
// 后续可能增加其他的信息展示
function createCarMarker(position) {
  // <div>速度：</div>
  const content = `<div class="custom-marker">
<img class="custom-marker-img" src="${MapCurrentCarMarkerSrc}">
</div>`
  const marker = new AMap.value.Marker({
    content: content, //自定义点标记覆盖物内容
    position,
    // anchor: 'middle-center',
    offset: new AMap.value.Pixel(-24, -44) //相对于基点的偏移位置
  })
  return marker
}
// 更新 marker 位置
function updateCarMarker(position, marker) {
  marker.setPosition(position)
}

async function getCarHistoryData(params: VehicleTrackQuery) {
  const nowDate = moment().format('YYYY-MM-DD HH:mm:ss')
  // 未完成的订单可能没有结束时间，写入当前时间为默认值
  const { startTime, endTime = nowDate, deviceId } = params
  if (!deviceId) return []
  // 请求轨迹数据
  const { result } = await getCarDataList({ startTime, endTime, deviceId })
  result.forEach(data => {
    data.date = moment(parseInt(data.gpsTime)).format('YYYY-MM-DD HH:mm:ss')
  })
  const overlays = []
  if (result.length) {
    const polyline = createMapPolyline(AMap.value, result)
    map.value.add(polyline)
    overlays.push(polyline)
    const startMarker = createMarker(AMap.value, result[0], 'map/start.png')
    map.value.add(startMarker)
    overlays.push(startMarker)
    const endMarker = createMarker(AMap.value, result[result.length - 1], 'map/end.png')
    map.value.add(endMarker)
    overlays.push(endMarker)
  }
  return [result, overlays]
}

function setView(overlays?: any) {
  map.value.setFitView(overlays)
}

// 获取围栏数据
async function getRailData(orderCode: string) {
  const { result } = await getWaybillRail({ oddCode: orderCode, oddType: 1 })
  const temp: RailData[] = []
  // 搅拌站围栏，即上货围栏
  if (result.mixingStationRails && result.mixingStationRails.length) temp.push(...initRailType(result.mixingStationRails, LoadType.Load))
  // 客户围栏，即卸货围栏
  if (result.customerRails && result.customerRails.length) temp.push(...initRailType(result.customerRails, LoadType.Unload))

  if (temp.length) {
    const rails = createRails(AMap.value, temp)
    map.value.add(rails)
    return rails
  }
  return []
}

function secToMins(time: string) {
  const secs = parseInt(time)
  return Math.ceil(secs / 60) + '分'
}
</script>
<style>
/* 高德弹框样式 */
.custom-marker {
  width: 48px;
  height: 48px;
}
.custom-marker-img {
  width: 48px;
  height: 48px;
}

@media only screen and (min-width: 1600px) {
  .custom-dialog-class {
    --el-dialog-width: 80%;
  }
}

@media only screen and (max-width: 1600px) {
  .custom-dialog-class {
    --el-dialog-width: 1280px;
  }
}
</style>
<style lang="scss" scoped>
#waybill-map {
  height: 600px;
}

#chart {
  height: 150px;
}

.steps {
  .step {
    box-sizing: border-box;
    padding-top: 70px;
    width: 0;
    height: 140px;
    flex-grow: 1;
    text-align: center;
    overflow: hidden;
    position: relative;
    .status {
      position: absolute;
      z-index: 1;
      display: inline-block;
      height: 10px;
      width: 10px;
      border-radius: 50%;
      background-color: #bbbcc4;
    }

    .title {
      margin-top: 10px;
    }
    &::before {
      content: '';
      width: 100%;
      position: absolute;
      left: -50%;
      top: calc(50% + 5px);
      border-top: 1px dashed #bbbcc4;
    }
    &:first-child {
      &::before {
        display: none;
      }
    }
    &::after {
      content: '';
      width: 100%;
      position: absolute;
      right: -50%;
      top: calc(50% + 5px);
      border-top: 1px dashed #bbbcc4;
      &:last-child {
        display: none;
      }
    }
    &:last-child {
      &::after {
        display: none;
      }
    }
    // 激活状态
    &.active {
      .status {
        background-color: #2768ff;
      }
      &::before {
        border-color: #2768ff;
      }
      &::after {
        border-color: #2768ff;
      }
    }
  }
  // 百分比计算
  // 容器左右 padding 14px
  // step 内容区宽度 100% - 28px
  // 再加上左边 padding 的 14px
  @for $i from 1 through 4 {
    > p.describe:nth-of-type(#{$i}) {
      left: calc($i / 5 * (100% - 28px) + 14px);
    }
  }

  .describe {
    position: absolute;
    max-width: calc((100% - 28px) / 5 - 4px);
    max-height: 140px;
    bottom: 70px;
    border-radius: 8px;
    background-color: #2768ff;
    color: #fff;
    font-weight: 600;
    padding: 6px;
    transform: translateX(-50%);
    &::after {
      content: '';
      position: absolute;
      bottom: -14px;
      width: 20px;
      height: 14px;
      border-top: 14px solid #2768ff;
      border-left: 10px solid transparent;
      border-right: 10px solid transparent;
      left: 50%;
      transform: translateX(-50%);
    }
  }
}

:deep(.detail-content) {
  width: calc((100% - 220px) / 2);
}
</style>
